package lg.iu.transforms;

import java.util.Iterator;

import lg.iu.ClassHooks;
import lg.iu.FieldHooks;

import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.MethodNode;

import wolf.iu.framework.ClassContainer;
import wolf.iu.framework.ClassesHandler;
import wolf.iu.framework.HooksList;
import wolf.iu.framework.hooks.AbstractTransform;
import wolf.iu.framework.hooks.HookState;
import wolf.iu.framework.insn.AnalyzerFactory;
import wolf.iu.framework.insn.InstructionSearcher;

public class ClientTransform extends AbstractTransform {

    @Override
    public HookState execute(final ClassesHandler ch, final ClassContainer cc, final HooksList hooks) {
        if (!cc.getSimpleName().equals("client"))
            return HookState.WrongClass;
        
        hooks.setClassHook(ClassHooks.Client, cc);
        
        final Iterator<MethodNode> methods = cc.getNode().methods.iterator();
        while (methods.hasNext()) {
            final MethodNode method = methods.next();
            final InstructionSearcher searcher = new InstructionSearcher(method);
            if (method.desc.equals("(B)Ljava/lang/String;")) {
                FieldInsnNode fin = (FieldInsnNode) searcher.getNext(AnalyzerFactory.GETSTATIC_L);
            
                if (fin != null) {
                    final ClassNode playerClass = ch.getClass(fin.desc);
                    final ClassNode characterClass = ch.getClass(playerClass.superName);
                    final ClassNode mobileClass = ch.getClass(characterClass.superName);
                    final ClassNode animableClass = ch.getClass(mobileClass.superName);
                    
                    hooks.setClassHook(ClassHooks.Player, playerClass); // TODO fin.desc?
                    hooks.setClassHook(ClassHooks.Character, characterClass);
                    hooks.setClassHook(ClassHooks.Mobile, mobileClass);
                    hooks.setClassHook(ClassHooks.Animable, animableClass);
                    
                    hooks.setFieldHook(ClassHooks.Client, FieldHooks.LocalPlayer, fin);
                }
                
                fin = (FieldInsnNode) searcher
                .getNext(AnalyzerFactory.GETSTATIC_I);
                /*if (fin != null) {
                    c.add("somePlayerInt", fin);
                }*/

                fin = (FieldInsnNode) searcher
                .getNext(AnalyzerFactory.GETFIELD_IA);
                if (fin != null) {
                    hooks.setFieldHook(ClassHooks.Character, FieldHooks.LocalX, fin);
                }

                fin = (FieldInsnNode) searcher
                .getNext(AnalyzerFactory.GETSTATIC_I);
                if (fin != null) {
                    hooks.setFieldHook(ClassHooks.Client, FieldHooks.BaseX, fin);
                }
                
                fin = (FieldInsnNode) searcher
                        .getNext(AnalyzerFactory.GETSTATIC_I);
                if (fin != null) {
                    hooks.setFieldHook(ClassHooks.Client, FieldHooks.BaseY, fin);
                }
                
                fin = (FieldInsnNode) searcher
                .getNext(AnalyzerFactory.GETFIELD_IA);
                if (fin != null) {
                    hooks.setFieldHook(ClassHooks.Character, FieldHooks.LocalY, fin);
                }
            }
            else if (method.desc.equals("(I)V")) {
                
                System.out.println("alala " + method.name);
                
                IntInsnNode iin = searcher.getNextInt(Opcodes.BIPUSH, 123);
                if (iin != null) {
                    final FieldInsnNode fin1 = (FieldInsnNode) searcher.getNext(AnalyzerFactory.GETSTATIC_I);
                    if (fin1 != null) {
                        hooks.setFieldHook(ClassHooks.Client, FieldHooks.GetCamPosX, fin1);
                    }
                }
                iin = searcher.getNextInt(Opcodes.BIPUSH, 242);
                if (iin != null) {
                    final FieldInsnNode fin1 = (FieldInsnNode) searcher.getNext(AnalyzerFactory.GETSTATIC_I);
                    if (fin1 != null) {
                        hooks.setFieldHook(ClassHooks.Client, FieldHooks.GetCamPosY, fin1);
                    }
                }
                
                //System.out.println("a " + method.name);
            }
            else if (Type.getArgumentTypes(method.desc).length > 6) {
                final IntInsnNode iin = searcher.getNextInt(Opcodes.SIPUSH, 16383);
                if (iin != null) {
                    FieldInsnNode fin = (FieldInsnNode) searcher.getNext(AnalyzerFactory.GETSTATIC_IA);
                    if (fin != null) {
                        hooks.setFieldHook(ClassHooks.Client, FieldHooks.GETCURVESIN, fin);
                    }
                    fin = (FieldInsnNode) searcher.getNext(AnalyzerFactory.GETSTATIC_IA);
                    if (fin != null) {
                        hooks.setFieldHook(ClassHooks.Client, FieldHooks.GETCURVECOS, fin);
                    }
                }
                searcher.resetToEnd();
                
                FieldInsnNode fin = (FieldInsnNode) searcher
                        .getPrevious(AnalyzerFactory.GETSTATIC_Z);
                fin = (FieldInsnNode) searcher
                    .getPrevious(AnalyzerFactory.GETSTATIC_Z);
                if (fin != null) {
                    hooks.setFieldHook(ClassHooks.Client,
                            FieldHooks.IsMenuOpen, fin);
                }
                //System.out.println(method.name + " ld");
            }
        }
        /*
        final Iterator<MethodNode> methods = cc.getASM().methods.iterator();
        while (methods.hasNext()) {
            final MethodNode method = methods.next();
            final InstructionFinder<AbstractInsnNode> searcher =
                new InstructionFinder<AbstractInsnNode>(method, true);
            
            if (method.desc.equals("(I)Ljava/lang/String;")) {
                FieldInsnNode fin = null;
                
                fin = (FieldInsnNode) searcher
                        .getNext(AnalyzerFactory.GETSTATIC_L);

                if (fin != null) {
                    c.add("localPlayer", fin);
                }
                if (player == null) {
                    player = hooks.add("Player", fin.desc);
                }
                /*
                fin = (FieldInsnNode) searcher
                .getNext(AnalyzerFactory.GETSTATIC_LA);
                
                if (fin != null) {
                    c.add("debugStrings", fin);
                }
                
                final IntInsnNode iin = (IntInsnNode) searcher.getNext(new InsnCondition<AbstractInsnNode>(Opcodes.BIPUSH) {
                    @Override
                    public boolean check(final AbstractInsnNode ain) {
                         return (ain instanceof IntInsnNode);
                    }
                });
                
                if (iin != null) {
                    System.out.println("MyPlayer loc in debug strings: " + iin.operand);
                }
                *
                fin = (FieldInsnNode) searcher
                .getNext(AnalyzerFactory.GETSTATIC_I);
                if (fin != null) {
                    c.add("somePlayerInt", fin);
                }

                fin = (FieldInsnNode) searcher
                .getNext(AnalyzerFactory.GETSTATIC_I);
                if (fin != null) {
                    c.add("baseX", fin);
                }
                
                //searcher.reset(true);
                //searcher.getNext(AnalyzerFactory.makeLDCCond("[1)"));

                fin = (FieldInsnNode) searcher
                .getNext(AnalyzerFactory.GETFIELD_IA);
                if (fin != null) {
                    if (character == null) {
                        character = hooks.add("Character", fin.owner);
                    }
                    character.add("localX", fin);
                }
                
                
                
                //fin = (FieldInsnNode) searcher
                //.getNext(AnalyzerFactory.GETFIELD_IA);
                /*if (fin != null) {
                    c.add("charYLocs", fin);
                }*

                fin = (FieldInsnNode) searcher
                        .getNext(AnalyzerFactory.GETSTATIC_I);
                if (fin != null) {
                    c.add("baseY", fin);
                }
                
                fin = (FieldInsnNode) searcher
                .getNext(AnalyzerFactory.GETFIELD_IA);
                if (fin != null) {
                    if (character == null) {
                        character = hooks.add("Character", fin.owner);
                    }
                    character.add("localY", fin);
                }

                searcher.getNext(AnalyzerFactory.makeIntCond(BIPUSH, 73));
                fin = (FieldInsnNode) searcher
                        .getNext(AnalyzerFactory.GETSTATIC_I);
                if (fin != null) {
                    c.add("loginIndex", fin); // TODO check
                }
            }
            /*else if (searcher.getNext(AnalyzerFactory.makeLDCCond("Jagex")) != null) {
                final FieldInsnNode fin = (FieldInsnNode) searcher.getNext(AnalyzerFactory.PUTSTATIC_SAA);
                if (fin != null) {
                    hooks.add("ObjectData", fin.owner);
                }
            }*
            else if (searcher.getNext(AnalyzerFactory.makeIntCond(BIPUSH, 46)) != null) {
                final FieldInsnNode fin = (FieldInsnNode) searcher.getNext(AnalyzerFactory.makeFieldCond(INVOKESTATIC, "(I)V"));
                if (fin != null) {
                    c.add("mouse", fin);
                }
            }
            else if (searcher.getNext(AnalyzerFactory.makeLDCCond("canvas")) != null) {
                
                
                FieldInsnNode fin = null;

                fin = (FieldInsnNode) searcher
                        .getNext(AnalyzerFactory.PUTSTATIC_L);

                if (fin != null) {
                    c.add("canvas", fin);
                }
            }
            else if (searcher.getNext(AnalyzerFactory.makeLDCCond("client.MA(")) != null) {
                // FIXME unstable ldc cond
                
                
                searcher.reset(true);
                final FieldInsnNode fin = (FieldInsnNode) searcher.getNext(AnalyzerFactory.GETSTATIC_LA);
                if (fin != null) {
                    c.add("npcNodes", fin);
                    hooks.add("NpcNode", fin.desc);
                }
                // pretty damn unstable cond
            }
            /*else if (method.desc.equals("(I)V")) {
                
                
                FieldInsnNode fin = null;
                
                searcher.reset(false);

                fin = (FieldInsnNode) searcher
                        .getPrev(AnalyzerFactory.GETFIELD_I);

                if (fin != null) {
                    hooks.add("Character", fin.owner);
                }
            }*
        }*/
        
        return HookState.Found;
    }

}
